using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using LegoBotPrimaAbstractions;

namespace LegoBotPrima.Actions
{
    public abstract class BotAction : /*CcrServiceBase,*/ IBotAction
    {
        public int ResultMessage { get; protected set; }
        private int _generation = 1;
        public int Generation { get { return this._generation; } }
        private IBotAction _previousAction;
        public IBotAction PreviousAction
        {
            get { return this._previousAction;  }
            set
            {
                if (value != null)
                {
                    if (this._previousAction != null)
                    {
                        throw new ArgumentException("BotAction.PreviousAction can only be assigned once");
                    }
                    this._generation = value.Generation + 1;
                }

                EnsureLinkedListSize(value);

                this._previousAction = value;
            }
        }
        public IBotAction RequestedNextAction { get; protected set; }
        public ILegoBotPrimaWorkflow Workflow { get; set; }
        public IWorkflowLogger Logger { get; set; }

        public virtual void Initialize() { }
        public virtual void Dispose() { }
        
        public virtual void OnCompassChanged(double degrees, DateTime timeStamp) { }
        public virtual void OnBumperChanged(bool touchSensorOn, DateTime timeStamp, string bumperName)
        {
            if(Workflow.Mode != RunMode.Autonomous)
            {
                return;
            }
            if(touchSensorOn)
            {
                Workflow.Stop();
                Workflow.SetAction(new StepBackBotAction() );
            }
        }
        public virtual void OnDistanceChanged(int distance, DateTime timeStamp) { }
        public virtual void OnAccelerometerChanged(double x, double y, double z, DateTime timeStamp) { }
        public virtual void OnTimerTick(int tickNimber) { }

        public bool IsRunning { get; internal set; }

        public virtual void MarkStart()
        {
            IsRunning = true;
        }
        public virtual void MarkStop()
        {
            IsRunning = false;
        }


        protected int SecondsAsTicks(int seconds)
        {
            return seconds*10;
        }
         

        private static void EnsureLinkedListSize(IEnumerable<IBotAction> action)
        {
            if(action == null)
            {
                return;
            }

            var maxGeneration = Config.Instance.MaxNumberPreviousActionsKept - 1;
            var i = 0;
            foreach (var prev in action)
            {
                if(++i >= maxGeneration)
                {
                    prev.PreviousAction = null;
                    break;
                }
            }
        }

        public IEnumerator<IBotAction> GetEnumerator()
        {
            return new Enumerator(this);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        [Serializable, StructLayout(LayoutKind.Sequential)]
        public struct Enumerator : IEnumerator<IBotAction>
        {
            private readonly IBotAction _first_action;
            private IBotAction _current;

            internal Enumerator(IBotAction action)
            {
                Ensure.IsNotNull(action, "action");
                this._first_action = action;
                this._current = null;
            }

            public void Dispose() { }

            public bool MoveNext()
            {
                if (this._current == null)
                {
                    this._current = this._first_action;
                    return true;
                }
                var prev = this._current.PreviousAction;
                if(prev != null)
                {
                    this._current = prev;
                    return true;
                }
                return false;
            }

            public void Reset()
            {
                this._current = null;
            }

            public IBotAction Current
            {
                get
                {
                    if (this._current == null)
                        throw new InvalidOperationException("You must call MoveNext before calling Current");
                    return this._current;
                }
            }

            object IEnumerator.Current
            {
                get { return Current; }
            }
        }
    }
}